// 进行封装 TCP socket 编写
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "logMessage.hpp"
#include "error.hpp"

#define gbacklog 5

class Sock
{
public:
    static int Socket()
    {
        // 1.创建套接字 面向字节流
        int listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
        {
            logMessage(FATAL, "Create Socket Fail!");
            exit(SOCKET_ERR);
        }
        logMessage(NORMAL, "Create Socket Success!");

        // 处理一下 Time_Wait 导致无法绑定端口号问题
        int opt = 1;
        setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        return listensock;
    }

    static void Bind(int sock, int port)
    {
        // 2.bind
        struct sockaddr_in peer;
        bzero(&peer, sizeof(peer));
        peer.sin_addr.s_addr = INADDR_ANY; // IP地址绑定任意的
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);

        if (bind(sock, (struct sockaddr *)&peer, sizeof(peer)) < 0)
        {
            logMessage(FATAL, "Bind Socket Fail!");
            exit(BIND_ERR);
        }
        logMessage(NORMAL, "Bind Socket Success!");
    }

    static void Listen(int sock)
    {
        // 3.Socket 设置为监听状态
        if (listen(sock, gbacklog) < 0)
        {
            // 设置失败
            logMessage(FATAL, "Listen Socket Fail!");
            exit(LISTEN_ERR);
        }
        logMessage(NORMAL, "Listen Socket Success!");
    }

    // 获取新连接
    static int Accpet(int listensock, uint16_t *clientPort, std::string *clientIP)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sock = accept(listensock, (struct sockaddr *)&peer, &len);
        if (sock < 0)
        {
            logMessage(FATAL, "Accept Socket Fail!");
            exit(ACCEPT_ERR);
        }
        else
        {
            logMessage(NORMAL, "Accpet Socket Success,get a new sock: %d", sock);
            *clientPort = ntohs(peer.sin_port);   // 网络转主机
            *clientIP = inet_ntoa(peer.sin_addr); // 网络转主机，再转字符串
        }

        return sock;
    }
};